﻿using FluentValidation;
using HandyControl.Controls;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;
using static Dm.util.ByteArrayQueue;
using ComboBox = System.Windows.Controls.ComboBox;
using DatePicker = System.Windows.Controls.DatePicker;
using PasswordBox = System.Windows.Controls.PasswordBox;
using TextBox = System.Windows.Controls.TextBox;

namespace YY.Admin.Core.FluentValidation
{
    public static class FluentValidationHelper
    {
        #region ✅ SkipValidation 附加属性
        public static readonly DependencyProperty SkipValidationProperty =
            DependencyProperty.RegisterAttached(
                "SkipValidation",
                typeof(bool),
                typeof(FluentValidationHelper),
                new System.Windows.PropertyMetadata(false));

        public static void SetSkipValidation(DependencyObject element, bool value) =>
            element.SetValue(SkipValidationProperty, value);

        public static bool GetSkipValidation(DependencyObject element) =>
            (bool)element.GetValue(SkipValidationProperty);
        #endregion

        /// <summary>
        /// 入口方法：从 ViewModel 自动获取 Validator，并绑定到所有子控件
        /// </summary>
        public static void Attach(FrameworkElement root, Type validatorOwnerType)
        {
            if (root == null || validatorOwnerType == null)
                return;

            // 找出 validator 属性
            var validatorProp = validatorOwnerType
                .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                .FirstOrDefault(p =>
                    typeof(IValidator).IsAssignableFrom(p.PropertyType) &&
                    p.Name.EndsWith("Validator", StringComparison.OrdinalIgnoreCase));

            if (validatorProp == null)
                return;

            var validatorInstance = root.DataContext != null
                ? validatorProp.GetValue(root.DataContext)
                : null;

            if (validatorInstance == null)
                return;

            var validatedType = validatorProp.PropertyType.GetInterfaces()
                .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IValidator<>))
                ?.GetGenericArguments()[0];

            if (validatedType == null)
                return;

            AttachRecursive(root, validatorInstance, validatedType);
        }

        /// <summary>
        /// 递归绑定所有子控件
        /// </summary>
        private static void AttachRecursive(DependencyObject parent, object validatorInstance, Type validatedType)
        {
            if (parent == null) return;

            // 先尝试走逻辑树 —— 能穿透 ScrollViewer、Row、Col、ContentPresenter 等容器
            foreach (var childObj in LogicalTreeHelper.GetChildren(parent).OfType<DependencyObject>())
            {
                var child = childObj;

                if (child is FrameworkElement feLogical)
                {
                    // ⚠️ 先设置 ErrorTemplate，保证 skip 的控件也能显示样式
                    var template = feLogical.TryFindResource("BottomLeftErrorTemplate_ForInfoElement") as ControlTemplate;
                    if (template != null)
                    {
                        Validation.SetErrorTemplate(feLogical, template);
                    }

                    // 🧭 再判断是否跳过校验
                    if (GetSkipValidation(feLogical))
                    {
                        AttachRecursive(child, validatorInstance, validatedType);
                        continue;
                    }

                    // 🪄 普通控件 / PasswordBox
                    AttachValidationForElement(feLogical, validatorInstance, validatedType);
                }

                // 递归逻辑树的子元素
                AttachRecursive(child, validatorInstance, validatedType);
            }

            // 再补充视觉树 —— 仅在 parent 是 Visual 或 Visual3D 时调用，避免 ColumnDefinition 等非 Visual 抛错
            if (parent is Visual || parent is System.Windows.Media.Media3D.Visual3D)
            {
                int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
                for (int i = 0; i < childrenCount; i++)
                {
                    var child = VisualTreeHelper.GetChild(parent, i);

                    if (child is FrameworkElement fe)
                    {
                        // ⚠️ 先设置 ErrorTemplate，保证 skip 的控件也能显示样式
                        var template = fe.TryFindResource("BottomLeftErrorTemplate_ForInfoElement") as ControlTemplate;
                        if (template != null)
                        {
                            Validation.SetErrorTemplate(fe, template);
                        }

                        // 🧭 再判断是否跳过校验
                        if (GetSkipValidation(fe))
                        {
                            AttachRecursive(child, validatorInstance, validatedType);
                            continue;
                        }

                        // 🪄 普通控件 / PasswordBox
                        AttachValidationForElement(fe, validatorInstance, validatedType);
                    }

                    // 递归子元素
                    AttachRecursive(child, validatorInstance, validatedType);
                }
            }
        }

        #region 普通控件处理

        private static void AttachValidationForElement(FrameworkElement fe, object validatorInstance, Type validatedType)
        {
            var localValues = fe.GetLocalValueEnumerator();
            while (localValues.MoveNext())
            {
                var entry = localValues.Current;
                var binding = BindingOperations.GetBinding(fe, entry.Property);
                if (binding == null) continue;

                AddValidationRule(binding, validatorInstance, validatedType);

                // 重新应用绑定才能生效
                BindingOperations.ClearBinding(fe, entry.Property);
                BindingOperations.SetBinding(fe, entry.Property, binding);
            }
        }

        private static void AddValidationRule(Binding binding, object validatorInstance, Type validatedType)
        {
            var propertyPath = binding.Path?.Path;
            if (string.IsNullOrEmpty(propertyPath)) return;

            var propertyName = propertyPath.Split('.').Last();
            var ruleType = typeof(FluentAutoValidationRule<>).MakeGenericType(validatedType);
            var rule = (ValidationRule)Activator.CreateInstance(ruleType)!;

            ruleType.GetProperty("Validator")?.SetValue(rule, validatorInstance);
            ruleType.GetProperty("PropertyName")?.SetValue(rule, propertyName);

            binding.ValidationRules.Add(rule);
        }

        #endregion

        #region 校验触发与跳过

        public static bool ValidateAll<T>(FrameworkElement root, T model, IValidator<T> validator) where T : class
        {
            // 1️⃣ 获取所有 skip 的属性名
            var skipPropertyNames = GetSkipValidationPropertyNames(root);

            // 2️⃣ 获取模型所有属性名
            var allPropertyNames = typeof(T)
                .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                .Select(p => p.Name)
                .ToList();

            // 3️⃣ 排除 skip，剩下的就是要验证的属性
            var includePropertyNames = allPropertyNames
                .Except(skipPropertyNames, StringComparer.Ordinal)
                .ToArray();

            // 4️⃣ 验证时只验证这些属性
            var result = validator.ValidateAsync(model, options =>
            {
                options.IncludeProperties(includePropertyNames);
            }).ConfigureAwait(false) // 同步等待Task（ValidationRule 不支持 async → 只能同步等待。）
            .GetAwaiter()
            .GetResult();

            // 5️⃣ 清除之前的错误
            ClearAllValidationErrors(root);

            // 6️⃣ 只对未 skip 的属性标记错误
            foreach (var error in result.Errors)
            {
                var controls = FindBoundControls(root, error.PropertyName);
                foreach (var control in controls)
                {
                    var depProperty = GetBoundDependencyProperty(control);
                    if (depProperty == null)
                        continue;

                    var bindingExpr = BindingOperations.GetBindingExpression(control, depProperty);
                    if (bindingExpr != null)
                    {
                        Validation.MarkInvalid(
                            bindingExpr,
                            new ValidationError(
                                new ExceptionValidationRule(),
                                bindingExpr,
                                error.ErrorMessage,
                                null));
                    }
                }
            }

            return result.IsValid;
        }

        private static HashSet<string> GetSkipValidationPropertyNames(DependencyObject root)
        {
            var skipPropertyNames = new HashSet<string>(StringComparer.Ordinal);

            foreach (var control in FindVisualChildren<FrameworkElement>(root))
            {
                if (!GetSkipValidation(control))
                    continue;

                var dp = GetBoundDependencyProperty(control);
                if (dp == null) continue;

                var binding = BindingOperations.GetBinding(control, dp);
                if (binding?.Path?.Path is string propertyPath && !string.IsNullOrEmpty(propertyPath))
                {
                    var propertyName = propertyPath.Split('.').Last();
                    skipPropertyNames.Add(propertyName);
                }
            }

            return skipPropertyNames;
        }

        public static void ClearAllValidationErrors(DependencyObject parent)
        {
            foreach (var control in FindVisualChildren<FrameworkElement>(parent))
            {
                if (GetSkipValidation(control))
                    continue;

                var depProperty = GetBoundDependencyProperty(control);
                if (depProperty == null) continue;

                var expr = BindingOperations.GetBindingExpression(control, depProperty);
                if (expr != null)
                    Validation.ClearInvalid(expr);
            }
        }

        #endregion

        #region 工具方法

        private static DependencyProperty? GetBoundDependencyProperty(FrameworkElement control)
        {
            return control switch
            {
                // 原生控件
                TextBox => TextBox.TextProperty,  
                // ✅ WPF 原生 PasswordBox（注意：WPF 原生 PasswordBox 没有现成的依赖属性绑定 Password，需要额外处理）
                PasswordBox => PasswordBoxHelper.PasswordProperty,
                ComboBox cb => BindingOperations.GetBinding(cb, Selector.SelectedItemProperty) != null
                ? Selector.SelectedItemProperty
                : BindingOperations.GetBinding(cb, Selector.SelectedValueProperty) != null
                    ? Selector.SelectedValueProperty
                    : null,
                DatePicker => DatePicker.SelectedDateProperty,
                CheckBox => ToggleButton.IsCheckedProperty,

                // HandyControl控件
                HandyControl.Controls.PasswordBox => HandyControl.Controls.PasswordBox.UnsafePasswordProperty,
                TimePicker => TimePicker.SelectedTimeProperty,
                NumericUpDown => NumericUpDown.ValueProperty,
                CheckComboBox => CheckComboBox.SelectedItemsProperty,

                _ => null
            };
        }

        private static IEnumerable<FrameworkElement> FindBoundControls(DependencyObject parent, string propertyName)
        {
            return FindVisualChildren<FrameworkElement>(parent)
                .Where(fe =>
                {
                    if (GetSkipValidation(fe)) return false;

                    var dp = GetBoundDependencyProperty(fe);
                    if (dp == null) return false;

                    var expr = BindingOperations.GetBindingExpression(fe, dp);
                    return expr != null && expr.ParentBinding.Path.Path.EndsWith(propertyName);
                });
        }

        private static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
        {
            if (depObj == null) yield break;

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                var child = VisualTreeHelper.GetChild(depObj, i);
                if (child is T tChild)
                    yield return tChild;

                foreach (var subChild in FindVisualChildren<T>(child))
                    yield return subChild;
            }
        }

        #endregion
    }
}
